package display;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

import common.Common;

import data.news.db.NewsDB;
import display.controller.BeltTimmer;
import display.controller.SentiWheelAnimator;
import display.controller.WheelSpeedAnimator;
import display.structure.Belt;
import display.structure.Wheel;

import prefuse.Constants;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.layout.graph.SquarifiedTreeMapLayout;
import prefuse.controls.ZoomControl;
import prefuse.data.Edge;
import prefuse.data.Graph;
import prefuse.data.Node;
import prefuse.data.Schema;
import prefuse.data.Tree;
import prefuse.data.Tuple;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.render.NullRenderer;
import prefuse.render.ShapeRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.EdgeItem;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualItem;
import prefuse.visual.VisualTable;
import prefuse.visual.expression.InGroupPredicate;
import prefuse.visual.sort.ItemSorter;
import weeny.pre.display.BaseUIDisplay;
import weeny.pre.util.Pre;
import weeny.recycle.MarkerHelper;

@SuppressWarnings("deprecation")
public class WheelDisplay extends BaseUIDisplay{
	
	public static int DISPLAY_WIDTH = 1000;
	public static long MAX_TIMESTEP = 40;
	public static String ENTITY_GROUP = "entity";
	
	public static String ROLL_ACTION = "move";
	
	public static String LABEL_GROUP = "label_group";
//	public static String CIRCLE_EDGE_GROUP = "circle_edge_group";
	
	public TupleSet focusTupleSet(String group){
		TupleSet result = getVisualization().getFocusGroup(group);
		if (result == null) {
			getVisualization().addFocusGroup(group);
			result = getVisualization().getFocusGroup(group);
		}
		return result;
	}
	public NodeItem node(String owner){
    	Graph g = (Graph) m_vis.getSourceData(ENTITY_GROUP);
    	VisualItem item = m_vis.getVisualItem(ENTITY_GROUP, g.addNode());
    	TupleSet ts = m_vis.getFocusGroup(owner);
    	if (ts == null) {
			m_vis.addFocusGroup(owner);
			ts = m_vis.getFocusGroup(owner);
		}
    	ts.addTuple(item);
    	item.set(WHEEL, owner);
    	return (NodeItem) item;
	}
	public boolean removeEdge(EdgeItem edge){
		Graph g = (Graph) m_vis.getSourceData(ENTITY_GROUP);
		return g.removeEdge((Edge)edge.getSourceTuple());
	}
    public EdgeItem edge(String owner){
    	NodeItem source = node(owner);
    	NodeItem target = node(owner);
    	EdgeItem edge = edge(source, target);
    	edge.setString(WHEEL, owner);
    	return edge;
    }
    public EdgeItem edge(NodeItem s, NodeItem t, String owner){
    	Graph graph = (Graph) getVisualization().getSourceData(ENTITY_GROUP);
    	Edge temp = graph.addEdge((Node) s.getSourceTuple(), (Node) t.getSourceTuple());
    	temp.setString(WHEEL, owner);
		return (EdgeItem) m_vis.getVisualItem(ENTITY_GROUP, temp);
    }
    
    public static String WHEEL_GROUP = "wheel_group";
    public void addWheel(Wheel wheel){
    	if (m_vis.getGroup(WHEEL_GROUP) == null) {
    		VisualTable vt = m_vis.addTable(WHEEL_GROUP);
    		vt.addColumn(WHEEL, Wheel.class);
    		DefaultRendererFactory drf = (DefaultRendererFactory) m_vis.getRendererFactory();
    		drf.add(new InGroupPredicate(WHEEL_GROUP), new ShapeRenderer(2));
    		final ItemSorter sorter = this.getItemSorter();
    		this.setItemSorter(new ItemSorter(){
    			@Override
    			public int score(VisualItem item) {
    				int score = sorter.score(item);
    				if (item.isInGroup(WHEEL_GROUP)) {
    					score /=2;
						score -=10;
					}
    				return score;
    			}
    		});
		}
    	VisualTable vt = (VisualTable) m_vis.getGroup(WHEEL_GROUP);
    	VisualItem shape = vt.addItem();
		shape.setShape(Constants.SHAPE_ELLIPSE);
		shape.set(WHEEL, wheel);
		shape.setSize(wheel.radius);
		Pre.setStkCol(shape, ColorLib.rgb(205, 94, 60));
		Pre.setFilCol(shape, ColorLib.color(Color.WHITE));
		Pre.setPos(shape, wheel.center);
    }
    public EdgeItem edge(NodeItem s, NodeItem t){
    	Graph graph = (Graph) getVisualization().getSourceData(ENTITY_GROUP);
    	Edge temp = graph.addEdge((Node) s.getSourceTuple(), (Node) t.getSourceTuple());
		return (EdgeItem) m_vis.getVisualItem(ENTITY_GROUP, temp);
    }

    public MarkerHelper mark = null;
    private Belt belt = null;
    
	public WheelDisplay(Visualization vis) {
		super(vis);
		this.setHighQuality(true);
		this.setSize(DISPLAY_WIDTH, 600);
//		addControlListener(new ZoomToFitControl());
//		addControlListener(new ZoomControl());
		
//		resetVisualization();
		resetVisualizationForMerck();
	}
	
	public static Schema NODE_SCHEMA = new Schema();
	public static Schema EDGE_SCHEMA = new Schema();
	public static String BAG = "bag";
	public static String BAG_BAR = "bag_bar";
	public static String WHEEL = "wheel";
	public static String TREEMAP = "TREES";
	public static String TEXT = "text";
	static {
		NODE_SCHEMA.addColumn(BAG, Tuple.class);
		NODE_SCHEMA.addColumn(TEXT, String.class);
		NODE_SCHEMA.addColumn(BAG_BAR, EdgeItem.class, null);
		NODE_SCHEMA.addColumn(WHEEL, String.class);
		EDGE_SCHEMA.addColumn(WHEEL, String.class);
		EDGE_SCHEMA.addColumn(TREEMAP, NodeItem.class, null);
	}
	public void resetVisualizationForMerck(){
		if (mark != null) {
			mark.clearAll();
		}
    	this.setVisualization(new Visualization());
    	mark = new MarkerHelper(m_vis, false);
    	Graph graph = new Graph(false);
    	graph.getNodeTable().addColumns(NODE_SCHEMA);
    	graph.getEdgeTable().addColumns(EDGE_SCHEMA);
    	getVisualization().addGraph(ENTITY_GROUP, graph);
    	
    	packTree = new Tree();
    	packTree.addRoot();
    	m_vis.addTree(TREE_GROUP, packTree);
    	tmLayout.setVisualization(getVisualization());
    	
    	DefaultRendererFactory drf = new DefaultRendererFactory(new ShapeRenderer(10));
    	LabelRenderer labelRen = new LabelRenderer(TEXT){
    		@Override
    		public void render(Graphics2D g, VisualItem item) {
    			double size = item.getSize();
    			if (size > 1.01) {
					this.setRenderType(RENDER_TYPE_FILL);
				}
    			else{
    				this.setRenderType(RENDER_TYPE_NONE);
    			}
    			super.render(g, item);
    		}
    	};
    	labelRen.setRoundedCorner(5, 5);
    	drf.add(new InGroupPredicate(LABEL_GROUP), labelRen);
    	
    	drf.add(new InGroupPredicate(TREE_GROUP+".nodes"), new NullRenderer(){
    		@Override
    		public void setBounds(VisualItem item) {}
    	});
    	getVisualization().setRendererFactory(drf);
    	
    	
    	Point2D center = new Point2D.Double(700,300);
    	Point2D otherEnd = new Point2D.Double(1200,300);
    	belt = new Belt(center, 200, 100, otherEnd, this);
		belt.loadDocInfo(Common.MERCK);
		belt.groupByDay(1);    
		belt.setupCurveDisplay();
    	belt.drawingBelt();
    	
    	ActionList move = new ActionList(ActionList.INFINITY);
    	move.add(new BeltTimmer(belt.name, belt));
    	
    	
    	Wheel drugWheel = new Wheel(this, belt);
    	drugWheel.loadKeys(Common.MERCK, "drug");
    	addWheel(drugWheel);
//    	WheelSpeedMover drugWheelMover = new WheelSpeedMover(drugWheel.name, -1, drugWheel);
//    	WheelSpeedMover drugWheelMover = new ForceWheelMover(drugWheel.name, drugWheel);
    	WheelSpeedAnimator drugWheelMover = new SentiWheelAnimator(drugWheel.name, drugWheel);
//    	WheelSpeedMover drugWheelMover = new StockWheelMover(drugWheel.name, drugWheel, belt);
    	move.add(drugWheelMover);


    	Wheel companyWheel = new Wheel(this, belt);
    	companyWheel.loadKeys(Common.MERCK, "company");
    	addWheel(companyWheel);
//    	WheelSpeedMover companyWheelMover = new WheelSpeedMover(companyWheel.name,-1, companyWheel);
//    	WheelSpeedMover companyWheelMover = new ForceWheelMover(companyWheel.name, companyWheel);
    	WheelSpeedAnimator companyWheelMover = new SentiWheelAnimator(companyWheel.name, companyWheel);
//    	WheelSpeedMover companyWheelMover = new StockWheelMover(companyWheel.name, companyWheel, belt);
    	move.add(companyWheelMover);

    	//for speed dump
//    	move.add(new DumpInfoTimmer("flush.txt", belt, drugWheelMover, companyWheelMover));
    	move.add(new RepaintAction());
    	getVisualization().putAction(ROLL_ACTION, move);
    	
    	belt.setupSpeedTableAccordingToKeyCnt();
//    	getVisualization().putAction("X", whee);
	}
    public void resetVisualization(){
    	if (mark != null) {
			mark.clearAll();
		}
    	this.setVisualization(new Visualization());
    	mark = new MarkerHelper(m_vis, false);
    	Graph graph = new Graph(false);
    	graph.getNodeTable().addColumns(NODE_SCHEMA);
    	graph.getEdgeTable().addColumns(EDGE_SCHEMA);
    	getVisualization().addGraph(ENTITY_GROUP, graph);
    	
    	packTree = new Tree();
    	packTree.addRoot();
    	m_vis.addTree(TREE_GROUP, packTree);
    	tmLayout.setVisualization(getVisualization());
    	
    	DefaultRendererFactory drf = new DefaultRendererFactory(new ShapeRenderer(10));
    	LabelRenderer labelRen = new LabelRenderer(TEXT){
    		@Override
    		public void render(Graphics2D g, VisualItem item) {
    			double size = item.getSize();
    			if (size > 1.01) {
					this.setRenderType(RENDER_TYPE_FILL);
				}
    			else{
    				this.setRenderType(RENDER_TYPE_NONE);
    			}
    			super.render(g, item);
    		}
    	};
    	labelRen.setRoundedCorner(5, 5);
    	drf.add(new InGroupPredicate(LABEL_GROUP), labelRen);
    	
    	drf.add(new InGroupPredicate(TREE_GROUP+".nodes"), new NullRenderer(){
    		@Override
    		public void setBounds(VisualItem item) {}
    	});
    	getVisualization().setRendererFactory(drf);
    	
    	
    	Point2D center = new Point2D.Double(700,300);
    	Point2D otherEnd = new Point2D.Double(1200,300);
    	belt = new Belt(center, 200, 100, otherEnd, this);
		belt.loadDocInfo(Common.MERCK);
		belt.groupByDay(1);    
		belt.setupCurveDisplay();
    	belt.drawingBelt();
    	
    	ActionList move = new ActionList(ActionList.INFINITY);
    	move.add(new BeltTimmer(belt.name, belt));
    	
    	
//    	Wheel drugWheel = new Wheel(this, belt);
//    	drugWheel.loadKeys(Common.MERCK, "drug");
//    	addWheel(drugWheel);
//    	
////    	WheelSpeedMover drugWheelMover = new WheelSpeedMover(drugWheel.name, -1, drugWheel);
////    	WheelSpeedMover drugWheelMover = new ForceWheelMover(drugWheel.name, drugWheel);
//    	WheelSpeedMover drugWheelMover = new SentiWheelMover(drugWheel.name, drugWheel);
////    	WheelSpeedMover drugWheelMover = new StockWheelMover(drugWheel.name, drugWheel, belt);
//    	move.add(drugWheelMover);

    	Wheel companyWheel = new Wheel(this, belt);
    	companyWheel.loadKeys(Common.MERCK, "company");
    	addWheel(companyWheel);
//    	WheelSpeedMover companyWheelMover = new WheelSpeedMover(companyWheel.name,-1, companyWheel);
//    	WheelSpeedMover companyWheelMover = new ForceWheelMover(companyWheel.name, companyWheel);
    	WheelSpeedAnimator companyWheelMover = new SentiWheelAnimator(companyWheel.name, companyWheel);
//    	WheelSpeedMover companyWheelMover = new StockWheelMover(companyWheel.name, companyWheel, belt);
    	move.add(companyWheelMover);
    	move.add(new RepaintAction());
    	getVisualization().putAction(ROLL_ACTION, move);
    	
    	belt.setupSpeedTableAccordingToKeyCnt();
    }

    
    private Tree packTree = null;
    private String TREE_GROUP = "pack";
    private SquarifiedTreeMapLayout tmLayout = new SquarifiedTreeMapLayout(TREE_GROUP);
	public static Map<String, Integer> type2Color = new HashMap<String, Integer>();
	static{
		type2Color.put("business", ColorLib.rgb(240,131,0));//yellow//Prefuse.randColor());
		type2Color.put("entertainment", ColorLib.rgb(233, 82, 149));//red//Prefuse.randColor());
		type2Color.put("news", ColorLib.rgb(105, 130, 27));//green//Prefuse.randColor());
		type2Color.put("other", ColorLib.rgb(30,80,162));//blue//Prefuse.randColor());
	}
    public void setupTreeMap(Tuple bagContent, EdgeItem bar, Belt belt){
    	NodeItem treeMapRoot = (NodeItem) m_vis.getVisualItem(TREE_GROUP, packTree.addChild(packTree.getRoot()));
    	Map<String, NodeItem> map = new HashMap<String, NodeItem>();
    	Tuple[] docTuples = belt.getDocTuples(bagContent);
    	if (docTuples == null || docTuples.length == 0) {
			return;
		}
    	for (Tuple tuple : docTuples) {
    		String type = tuple.getString(NewsDB.FLD_PRE_TYPE);
			NodeItem r = null;
			if (map.containsKey(type)) {
				r = map.get(type);
			}
			else{
				Node typeRoot = packTree.addChild((Node)treeMapRoot.getSourceTuple());
				NodeItem visualTypeRoot = (NodeItem) m_vis.getVisualItem(TREE_GROUP,typeRoot);
				map.put(type, visualTypeRoot);
				r = visualTypeRoot;
			}
			Node docNode = packTree.addChild((Node)r.getSourceTuple());
			NodeItem vDocNode = (NodeItem) m_vis.getVisualItem(TREE_GROUP, docNode);
			Pre.setFilCol(vDocNode, type2Color.get(type));
			Pre.setStkCol(vDocNode, ColorLib.rgb(84, 63, 50));
		}
		tmLayout.setLayoutBounds(new Rectangle2D.Double(0,0,bar.getSize()+2,belt.getBagLength(bagContent)));
		tmLayout.setLayoutRoot(treeMapRoot);
		tmLayout.run(0);
		bar.set(TREEMAP, treeMapRoot);
    }
    private void rewind(){
    	belt.setSpeed(0-belt.getSpeed());
    }
    public JPanel displayPane(){
    	JPanel displayPane = new JPanel();
		displayPane.setLayout(new BoxLayout(displayPane, BoxLayout.Y_AXIS));
		displayPane.add(this);
		if(belt.curveDisplay != null)
			displayPane.add(belt.curveDisplay);
		return displayPane;
    }
    public static void main(String[] args) {
//    	double[] temp = new double[]{
//    			10,10,10,10,86,15,40,10,30,20, 60, 60
//    	};
//    	double avg = 0;
//    	for (double d : temp) {
//			avg += d;
//		}
//    	avg /= temp.length;
//    	double sum = 0;
//    	for (double d : temp) {
//			sum+=(d-avg)*(d-avg);
//		}
//    	System.out.println(Math.sqrt(sum/temp.length));
//    	if (true) {
//			return;
//		}
		final WheelDisplay wheelDisplay = new WheelDisplay(new Visualization());
		wheelDisplay.addControlListener(new ZoomControl());
		JFrame frame = new JFrame("TextWheel");
		JPanel contentPane = new JPanel(new BorderLayout());
		JPanel controlPane = new JPanel();
		controlPane.setLayout(new BoxLayout(controlPane, BoxLayout.Y_AXIS));
		controlPane.setBorder(new LineBorder(Color.DARK_GRAY));
		controlPane.setBackground(Color.WHITE);
		controlPane.add(wheelDisplay.createSaveImageBox("TextWheel"));
		controlPane.add(wheelDisplay.belt.curveDisplay.createSaveImageBox("Curve"));
		{
			JButton f = new JButton("run / pause");
			f.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					if (!wheelDisplay.getVisualization().getAction(ROLL_ACTION).isRunning()) {
						wheelDisplay.getVisualization().run(ROLL_ACTION);
					}
					else{
						wheelDisplay.getVisualization().cancel(ROLL_ACTION);
					}
//					if (!wheelDisplay.getVisualization().getAction("X").isRunning()) {
//						wheelDisplay.getVisualization().run("X");
//					}
//					else{
//						wheelDisplay.getVisualization().cancel("X");
//					}
				}
			});
			controlPane.add(f);
		}
		{
			JButton f = new JButton("rewind");
			f.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					wheelDisplay.rewind();
				}
			});
			controlPane.add(f);
		}
		JPanel temp = wheelDisplay.displayPane();
		temp.doLayout();
		contentPane.add(temp, BorderLayout.CENTER);
		System.out.println(temp.getComponentCount()+"<<");
		System.out.println(temp.getComponent(0).getX());
		System.out.println(temp.getComponent(0).getY());
		System.out.println(temp.getComponent(1).getX());
		System.out.println(temp.getComponent(1).getY());
		System.out.println(temp.getComponent(0).getAlignmentX());
		System.out.println(temp.getComponent(0).getAlignmentY());
		System.out.println(temp.getComponent(1).getAlignmentX());
		System.out.println(temp.getComponent(1).getAlignmentY());
		System.out.println(temp.getComponent(0).getBounds());
		System.out.println(temp.getComponent(1).getBounds());
		contentPane.add(controlPane, BorderLayout.EAST);
		frame.getContentPane().add(contentPane);
		frame.pack();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}